package ginutil

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"mime"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"

	"gitee.com/tomatomeatman/golang-repository/bricks/model/emity"
	Log "github.com/cihub/seelog"
	"github.com/gin-contrib/static"
	"github.com/gin-gonic/gin"
	"gopkg.in/ini.v1"
)

type Context = *gin.Context

type GinUtil struct{}

// 请求类型枚举
type RequestType string

const (
	GET     RequestType = "GET"
	POST    RequestType = "POST"
	DELETE  RequestType = "DELETE"
	PUT     RequestType = "PUT"
	OPTIONS RequestType = "OPTIONS"
)

// 枚举转字符串
func (rt RequestType) String() string {
	switch rt {
	case GET:
		return "GET"
	case POST:
		return "POST"
	case DELETE:
		return "DELETE"
	case PUT:
		return "PUT"
	case OPTIONS:
		return "OPTIONS"
	default:
		return "NA"
	}
}

var (
	guiUtil_UrlRegeditWg sync.WaitGroup                    //同步原语,要保证所有的Url注册都已经完成
	guiUtil_CheckWrite   sync.Mutex                        //保存锁
	httpHandleFunc       = make(map[string]HttpHandleInfo) //http控制层接口信息集合
	appGinServer         *http.Server
)

// http控制层接口信息
type HttpHandleInfo struct {
	Url  string
	Type RequestType
	Fun  func(ctx Context) interface{}
}

// 设置跨域
func SetCors(r *gin.Engine) {
	r.Use(Cors())
}

func GetGin(ginMode, webRoot, Port string, InterceptorFunc func(ctx Context) bool) *http.Server {
	HttpHandleInfos := GetController()

	switch strings.ToUpper(ginMode) { //gin运行模式
	case "RELEASE":
		gin.SetMode(gin.ReleaseMode)
	case "TEST":
		gin.SetMode(gin.TestMode)
	case "DEBUG":
		gin.SetMode(gin.DebugMode)
	default:
		gin.SetMode(gin.ReleaseMode)
	}

	r := gin.Default()

	SetCors(r)                //设置跨域
	SetStatic(r, "."+webRoot) //设置静态目录

	r.GET("/shutdown", Shutdown)
	r.POST("/shutdown", Shutdown)

	for _, val := range HttpHandleInfos {
		switch val.Type {
		case GET:
			r.GET(val.Url, GetHandleFunc(val.Url, GET, InterceptorFunc, HttpHandleInfos))
		case POST:
			r.POST(val.Url, GetHandleFunc(val.Url, POST, InterceptorFunc, HttpHandleInfos))
		case DELETE:
			r.DELETE(val.Url, GetHandleFunc(val.Url, DELETE, InterceptorFunc, HttpHandleInfos))
		case PUT:
			r.PUT(val.Url, GetHandleFunc(val.Url, PUT, InterceptorFunc, HttpHandleInfos))
		case OPTIONS:
			r.OPTIONS(val.Url, GetHandleFunc(val.Url, OPTIONS, InterceptorFunc, HttpHandleInfos))
		default:
			r.GET(val.Url, func(ctx Context) {
				ctx.JSONP(http.StatusBadRequest, emity.MsgEmity{}.Err(1001, "请求方式错误,不支持此方式"))
			})
		}
	}

	httpHandleFunc = make(map[string]HttpHandleInfo) //清理

	appGinServer := &http.Server{
		Addr:    ":" + Port,
		Handler: r,
	}

	return appGinServer
}

// 跨域函数
func Cors() gin.HandlerFunc {
	return func(c *gin.Context) {
		method := c.Request.Method
		origin := c.Request.Header.Get("Origin")
		if origin != "" {
			c.Header("Access-Control-Allow-Origin", "*") // 可将将 * 替换为指定的域名
			c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
			//c.Header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization")
			//c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type")
			c.Header("Access-Control-Allow-Headers", "*")
			c.Header("Access-Control-Expose-Headers", "*")
			c.Header("Access-Control-Allow-Credentials", "true")
		}
		if method == "OPTIONS" {
			c.AbortWithStatus(http.StatusNoContent)
		}
		c.Next()
	}
}

// 设置静态路径
func SetStatic(r *gin.Engine, webroot string) {
	_ = mime.AddExtensionType(".js", "application/javascript") // 特殊处理JS文件的Content-Type
	r.Use(static.Serve("/", static.LocalFile(webroot, true)))  // 前端项目静态资源
}

// 注册web接口
func RegisterController(url string, iTypes interface{}, handler func(ctx Context) interface{}) {
	RegisterControllers(url, iTypes, handler)
}

// 注册web接口
func ControllerRegister(url string, handler func(ctx Context) interface{}, iTypes ...RequestType) {
	if len(iTypes) < 1 {
		registerController(url, POST, handler)
		return
	}

	for _, rt := range iTypes {
		registerController(url, rt, handler)
	}
}

// 注册web接口
func RegisterControllers(url string, iTypes interface{}, handler func(ctx Context) interface{}) {
	switch t := iTypes.(type) {
	case RequestType:
		registerController(url, t, handler)
	case []RequestType:
		if len(iTypes.([]RequestType)) < 1 {
			registerController(url, POST, handler)
			return
		}

		for _, v := range iTypes.([]RequestType) {
			registerController(url, v, handler)
		}
	default:
		registerController(url, POST, handler)
	}
}

// 注册web接口
func registerController(url string, iType RequestType, handler func(ctx Context) interface{}) {
	guiUtil_UrlRegeditWg.Add(1)       // 注册前增加计数器
	defer guiUtil_UrlRegeditWg.Done() // 注册结束时减少计数器

	guiUtil_CheckWrite.Lock()         //加锁
	defer guiUtil_CheckWrite.Unlock() //解锁

	temp := url + iType.String()
	_, ok := httpHandleFunc[temp]
	if ok {
		Log.Error("链接重复:" + url)
		return
	}

	httpHandleFunc[temp] = HttpHandleInfo{
		Url:  url,
		Type: iType,
		Fun:  handler,
	}
}

// 取注册的web接口
func GetController() map[string]HttpHandleInfo {
	guiUtil_UrlRegeditWg.Wait() // 等待所有注册完成
	return httpHandleFunc
}

/**
 * 取注册的web接口
 */
func GetHandleFunc(urlKey string, iType RequestType, InterceptorFunc func(ctx Context) bool,
	controllerMap map[string]HttpHandleInfo) func(ctx Context) {
	result := func(ctx Context) {
		urlStr := ctx.Request.URL.Path

		if nil != InterceptorFunc {
			if !InterceptorFunc(ctx) {
				return
			}
		}

		var urlAndType string
		if strings.HasPrefix(urlStr, "/proxy/") { //路径中存在转发请求
			urlAndType = "/proxy/*proxy" + iType.String()
		} else {
			urlAndType = urlKey + iType.String() //url和请求方式组合
		}

		httpHandleInfo, ok := controllerMap[urlAndType] //给个默认
		if !ok {
			return
		}
		// for runKey := range controllerMap {
		// 	if runKey != urlStr {
		// 		continue
		// 	}

		// 	httpHandleInfo = controllerMap[urlAndType]
		// 	vb = true
		// 	break
		// }

		// if !vb {
		// 	return
		// }

		obj := httpHandleInfo.Fun(ctx)
		switch obj.(type) {
		case *emity.MsgEmity, emity.MsgEmity:
			ctx.JSONP(http.StatusOK, obj)
		case *emity.FileEmity:
			fm := obj.(*emity.FileEmity)
			if !fm.Gsuccess {
				ctx.JSONP(http.StatusOK, obj)
				break
			}

			if fm.Gdisposition != "" {
				ctx.Header("Content-Disposition", fm.Gdisposition)
			}

			ctx.Header("Content-Type", fm.Gtype)
			ctx.Header("Content-Length", fm.GsLength)
			ctx.Writer.Write(fm.Gdata.([]byte))
		case emity.FileEmity:
			fm := obj.(emity.FileEmity)
			if !fm.Gsuccess {
				ctx.JSONP(http.StatusOK, obj)
				break
			}

			if fm.Gdisposition != "" {
				ctx.Header("Content-Disposition", fm.Gdisposition)
			}

			ctx.Header("Content-Type", fm.Gtype)
			ctx.Header("Content-Length", fm.GsLength)
			ctx.Writer.Write(fm.Gdata.([]byte))
		case []byte:
			by := obj.([]byte)
			ctx.Header("Content-Type", "application/octet-stream")
			ctx.Header("Content-Length", strconv.Itoa(len(by)))
			ctx.Writer.Write(by)
		default:
			ctx.Writer.Write([]byte(fmt.Sprintf("%v", obj)))
		}
	}

	return result
}

// 关闭服务
func Shutdown(ctx Context) {
	key := getParam(ctx, "sInsideKey")
	if key == "" {
		ctx.JSONP(http.StatusOK, emity.MsgEmity{}.Err(100001, "关闭密钥参数sInsideKey缺失"))
		return
	}

	appKey := readConfigKey("App", "InsideKey", "12345")
	if appKey != key {
		ctx.JSONP(http.StatusOK, emity.MsgEmity{}.Err(100001, "关闭密钥参数错误"))
		return
	}

	Log.Debug("关闭服务...")

	if err := appGinServer.Shutdown(ctx); err != nil {
		Log.Debug("服务强制关闭发生异常:", err)
		ctx.JSONP(http.StatusOK, emity.MsgEmity{}.Err(100003, "服务强制关闭发生异常"))
		return
	}

	fmt.Println("服务关闭")
	ctx.JSONP(http.StatusOK, emity.MsgEmity{}.Success(9999, "服务强制关闭成功"))
}

/**
 * 取参数
 * ctx GinHttp上下文对象
 * name 参数名称
 * def 默认值
 */
func getParam(ctx Context, name string) string {
	ctx.Request.ParseForm() //警告:必须先 解析所有请求数据

	//-- 取POST方法的参数 --//
	params := make(map[string]interface{})

	br, _ := io.ReadAll(ctx.Request.Body)
	ctx.Request.Body.Close()
	ctx.Request.Body = io.NopCloser(bytes.NewBuffer(br))

	json.NewDecoder(bytes.NewBuffer(br)).Decode(&params)
	temp, ok := params[name]
	if ok {
		return fmt.Sprint(temp)
	}

	//-- 取GET方法的参数 --//
	query := ctx.Request.URL.Query() // 获取请求的参数

	v := query[name]
	if nil != v && len(v) > 0 && strings.TrimSpace(v[0]) != "" {
		return v[0]
	}

	return ""
}

/**
 * 读取配置文件
 * section 配置文件section
 * key 配置文件key
 * def 默认值
 */
func readConfigKey(section, key, def string) string {
	root := ""
	exePath, err := os.Executable()
	if err != nil {
		root = "."
	}

	root, _ = filepath.EvalSymlinks(filepath.Dir(exePath))

	configFilePath := strings.Replace(root+"/config/app.ini", "\\", "/", -1)

	_, err = os.Stat(configFilePath) //os.Stat获取文件信息
	if err != nil {
		if !os.IsExist(err) {
			Log.Error("配置文件不存在", err)
			return def
		}
	}

	// 加载配置文件
	conf, err := ini.Load(configFilePath)
	if err != nil {
		Log.Error("读取配置文件失败:", err)
		return def
	}

	// 获取配置文件中的 section 和 key
	appSection := conf.Section(section)
	result, err := appSection.GetKey(key)
	if err != nil {
		Log.Error("读取配置项失败:", err)
		return def
	}

	return fmt.Sprintf("%v", result)
}
